package com.bsj.sipgateway.sip.event.offline;

import com.alibaba.fastjson.JSON;
import com.bsj.power.common.config.redis.RedisConstant;
import com.bsj.power.common.config.rocketmq.producer.BaseMqPushUtil;
import com.bsj.power.common.constant.AlarmEnum;
import com.bsj.power.common.constant.MqTopicAndTagConstants;
import com.bsj.power.common.constant.NumericalConstants;
import com.bsj.power.common.def.dto.DeviceRegisterMidDTO;
import com.bsj.power.common.def.dto.RedisDeviceInfo;
import com.bsj.power.common.def.entity.alarmReport.AlarmReport;
import com.bsj.power.common.def.entity.gb28181.Device;
import com.bsj.power.common.def.entity.log.VideoOpLog;
import com.bsj.power.common.def.enums.VideoOpTypeEnum;
import com.bsj.power.common.def.vo.WebSocketMsgVO;
import com.bsj.power.common.mapper.AlarmReportMapper;
import com.bsj.power.common.mapper.DeviceMapper;
import com.bsj.power.common.mapper.FaultMapper;
import com.bsj.power.common.mapper.VideoOpLogMapper;
import com.bsj.power.common.util.ExceptionUtil;
import com.bsj.sipgateway.cache.DeviceCache;
import com.bsj.sipgateway.common.constant.Constants;
import com.bsj.sipgateway.sip.session.VideoStreamSessionManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import redis.clients.bsj.JedisClusterBSJ;

import java.util.Date;

/**
 * @author bsj-chenjunkang
 * @Description: 离线事件监听器，监听到离线后，修改设备离在线状态。 设备离线有两个来源：
 * 1、设备主动注销，发送注销指令，{@link com.bsj.sipgateway.sip.transmit.request.impl.RegisterRequestProcessor}
 * 2、设备未知原因离线，心跳超时,{@link com.bsj.sipgateway.sip.event.offline.OfflineEventListener}
 * @date 2022/8/31
 */
@Slf4j
@Component
public class OfflineEventListener implements ApplicationListener<OfflineEvent> {

    @Autowired
    private VideoStreamSessionManager streamSession;

    @Autowired
    private JedisClusterBSJ jedisClusterBSJ;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private VideoOpLogMapper videoOpLogMapper;

    @Autowired
    private BaseMqPushUtil baseMqPushUtil;

    @Autowired
    private FaultMapper faultMapper;

    @Autowired
    private AlarmReportMapper alarmReportMapper;

    @Value("${sip.cvrIds}")
    private String cvrIds;

    @Override
    public void onApplicationEvent(OfflineEvent event) {
//        String[] split = cvrIds.split(",");
//        List<String> strings = Arrays.asList(split);
//        if (strings.contains(event.getDeviceId())) {
//            log.info("cvr状态待检测，cvrId={}", event.getDeviceId());
//            return;
//        }

        removeAllCache(event.getDeviceId());
        Device device = deviceMapper.getDeviceByDeviceId(event.getDeviceId());
        if (device == null) {
            return;
        }
        Long did = device.getId();
        // 删除redis中直播已存在的状态
        jedisClusterBSJ.hdel(RedisConstant.VIDEO_PLAY_STATUS, did + "-1");
        jedisClusterBSJ.hdel(RedisConstant.VIDEO_PLAY_STATUS, did + "-2");

        switch (event.getFrom()) {
            // 心跳超时触发的离线事件，说明deviceCache中已删除，无需处理
            case Constants.EVENT_OUTLINE_TIMEOUT:
                log.info("监听到设备{}心跳超时离线", event.getDeviceId());

                // 把设备离线记录到日志中
                Date date = new Date();
                try {
                    VideoOpLog videoOpLog = new VideoOpLog();
                    int type = VideoOpTypeEnum.DEVICE_LOGOUT_ABNORMAL.getOpType();
                    videoOpLog.setDeviceId(device.getDeviceId());
                    videoOpLog.setName(VideoOpTypeEnum.getByValue(type));
                    videoOpLog.setOpUserId(2L);
                    videoOpLog.setOpUserName("admin");
                    videoOpLog.setOpType(type);
                    videoOpLog.setOpTime(date);
                    videoOpLog.setContent("设备异常离线：" + device.getNickName());
                    videoOpLog.setNickName(device.getNickName());
                    videoOpLog.setEquipmentNumber(device.getEquipmentNumber());
                    videoOpLogMapper.insert(videoOpLog);
                } catch (Exception e) {
                    log.error("记录设备异常离线日志失败：{}", ExceptionUtil.getStackStr(e));
                }

                // 存储报警信息、报警分类
                AlarmReport alarmReport = new AlarmReport();
                // todo 填充数据
                alarmReport.setDeviceName(device.getDeviceId());
                alarmReport.setNikeName(device.getNickName());
                alarmReport.setDeviceType(4); // GB28181设备
                alarmReport.setAlarmType(AlarmEnum.DEVICE_ABNORMAL_OFFLINE.getCode());
                alarmReport.setBeginTime(date);
                alarmReport.setEndTime(date);
                alarmReport.setRemark(AlarmEnum.DEVICE_ABNORMAL_OFFLINE.getName());
                alarmReport.setHandleState(1);
                alarmReportMapper.insert(alarmReport);

                // 将报警数据推送到前端
                //报警提醒
                WebSocketMsgVO webSocketMsgVO = WebSocketMsgVO.builder()
                        .msgType(NumericalConstants.THREE)
                        .obj(alarmReport)
                        .build();
                baseMqPushUtil.sendTopicBytes(MqTopicAndTagConstants.SYSTEM_WEB_SERVER_CHANGE_TOPIC,
                        MqTopicAndTagConstants.SYSTEM_WEB_PRODUCT_CHANGE_TAG, JSON.toJSONBytes(webSocketMsgVO));

                //发送消息通知组合报警
                baseMqPushUtil.sendTopicBytes(MqTopicAndTagConstants.BASE_SERVER_PLS_CHANGE_TOPIC,
                        MqTopicAndTagConstants.BASE_CHANGE_PLS_UPDATE_TAG, JSON.toJSONBytes(alarmReport));

                addMidLogoutTime(did);

                updateRedisDeviceInfo(event.getDeviceId(), true);
                break;
            // 设备主动注销触发的离线事件，需要删除deviceCache中的超时监听
            case Constants.EVENT_OUTLINE_UNREGISTER:
                log.info("正常收到设备{}注销消息", event.getDeviceId());
                addMidLogoutTime(did);

                updateRedisDeviceInfo(event.getDeviceId(), false);
                DeviceCache.removeDevice(event.getDeviceId());
                break;
            default:
//                DeviceCache.removeDevice(event.getDeviceId());
        }
        // 处理离线监听
//		storager.outline(event.getDeviceId());
    }

    private void removeAllCache(String deviceId) {
        //简单粗暴，删除1到4
        for (int i = 1; i <= 4; i++) {
            streamSession.remove(deviceId, Constants.PLAYER_PREFIX, "" + i);
            streamSession.remove(deviceId, Constants.PLAY_BLACK_PREFIX, "" + i);
            streamSession.remove(deviceId, Constants.BROADCAST_PREFIX, "" + i);
        }
    }


    private void addMidLogoutTime(Long did) {
        // 不管是正常退出还是异常退出，只要是注销了，只做两件事1是写入持续运行时间2是写入注销时间
        String hget = jedisClusterBSJ.hget(RedisConstant.DEVICE_REGISTER_MID_DTO, did + "");
        // 如果没有，直接不处理，让它再注册的时候写进去
        if (hget == null || "".equals(hget)) {
            return;
        }
        DeviceRegisterMidDTO midDTO = JSON.parseObject(hget, DeviceRegisterMidDTO.class);
        // 1写入持续运行的时间
        Long registerTime = midDTO.getRegisterTime();
        long now = System.currentTimeMillis();
        long duration = now - registerTime;
        midDTO.setOnlineTime(midDTO.getOnlineTime() + duration);
        // 2写入注销时间
        midDTO.setLogoutTime(now);
        jedisClusterBSJ.hset(RedisConstant.DEVICE_REGISTER_MID_DTO, did + "", JSON.toJSONString(midDTO));
    }

    private void updateRedisDeviceInfo(String deviceId, boolean deviceAbnormalOfflineFlag) {
        String diStr = jedisClusterBSJ.get(RedisConstant.REDIS_DEVICE_INFO + deviceId);
        if (diStr == null) {
            return;
        }
        RedisDeviceInfo redisDeviceInfo = JSON.parseObject(diStr, RedisDeviceInfo.class);
        redisDeviceInfo.setLogoutTime(System.currentTimeMillis());
        redisDeviceInfo.setOnline(Constants.DEVICE_OFFLINE);
        if (deviceAbnormalOfflineFlag) {
            redisDeviceInfo.setDeviceAbnormalOffline(1);
        }
        jedisClusterBSJ.set(RedisConstant.REDIS_DEVICE_INFO + deviceId, JSON.toJSONString(redisDeviceInfo));
    }
}
